From 31a0a5486f2348823e211621be1fa33ff5fdabb1 Mon Sep 17 00:00:00 2001 From: robertl Date: Thu, 5 Oct 2006 04:07:34 +0000 Subject: [PATCH] Make xasprintf work sensibly in light of both (sigh) C99 and SUSv1 definitions of snprintf. Probably needs more abstraction work, but this is more correct in more cases. --- util.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/util.c b/util.c index d2e48bd4a..7dfcb446d 100644 --- a/util.c +++ b/util.c @@ -293,21 +293,75 @@ xfputs(const char *errtxt, const char *s, FILE *stream) int xasprintf(char **strp, const char *fmt, ...) { + /* From http://perfec.to/vsnprintf/pasprintf.c */ +/* size of first buffer malloc; start small to exercise grow routines */ +#define FIRSTSIZE 64 va_list args; - int res; - - va_start(args, fmt); - res = vsnprintf((char *)NULL, 0, fmt, args); - *strp = xmalloc(res + 1); - va_end(args); - - va_start(args, fmt); - res = vsnprintf(*strp, res + 1, fmt, args); - va_end(args); + char *buf; + size_t bufsize; + char *newbuf; + size_t nextsize; + int outsize; + + bufsize = 0; + for (;;) { + if (bufsize == 0) { + if ((buf = xmalloc(FIRSTSIZE)) == NULL) { + *strp = NULL; + return -1; + } + bufsize = 1; + } else if ((newbuf = xrealloc(buf, nextsize)) != NULL) { + buf = newbuf; + bufsize = nextsize; + } else { + xfree(buf); + *strp = NULL; + return -1; + } - is_fatal(res < 0, "(internal): vsnprintf returned %d!", res); - - return res; + va_start(args, fmt); + outsize = vsnprintf(buf, bufsize, fmt, args); + va_end(args); + + if (outsize == -1) { + /* Clear indication that output was truncated, but no + * clear indication of how big buffer needs to be, so + * simply double existing buffer size for next time. + */ + nextsize = bufsize * 2; + + } else if (outsize == bufsize) { + /* Output was truncated (since at least the \0 could + * not fit), but no indication of how big the buffer + * needs to be, so just double existing buffer size + * for next time. + */ + nextsize = bufsize * 2; + + } else if (outsize > bufsize) { + /* Output was truncated, but we were told exactly how + * big the buffer needs to be next time. Add two chars + * to the returned size. One for the \0, and one to + * prevent ambiguity in the next case below. + */ + nextsize = outsize + 2; + + } else if (outsize == bufsize - 1) { + /* This is ambiguous. May mean that the output string + * exactly fits, but on some systems the output string + * may have been trucated. We can't tell. + * Just double the buffer size for next time. + */ + nextsize = bufsize * 2; + + } else { + /* Output was not truncated */ + break; + } + } + *strp = buf; + return 0; } /* -- 2.30.2